#include <linux/module.h>
#include <linux/poll.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <asm/current.h>
#include <linux/delay.h>
static int major;
static struct class *dht11_class;
static struct gpio_desc *dht11_out;
static int irq;
static unsigned char temprature_int;
static unsigned char temprature_float;
static unsigned char humidity_int;
static unsigned char humidity_float;
static unsigned char check_byte;

static unsigned char data[5];
static unsigned char databit[40];
static unsigned char databit1_count[40];
static unsigned char databit0_count[40];
int bit_index = 0;
int times[40];
u64 t1, t2;
static void delay_us(int us)
{
    u64 pre, last;
    pre = ktime_get_boot_ns();
    while (1)
    {
        last = ktime_get_boot_ns();
        if (last - pre >= us * 1000)
            break;
    }
}

int read_byte(unsigned char *byte)
{
    int i;
    for (i = 0; i < 8; i++)
    {
        int hight_count = 0;
        int low_bit = 0;
        int time_out = 200;
        // while (gpiod_get_value(dht11_out) == 1 && --time_out)
        // {
        //     delay_us(1);
        // }
        // if (!time_out)
        // {
        //     return -EAGAIN;
        // }
        // time_out = 200;
        t1 = ktime_get_boot_ns();
        while (gpiod_get_value(dht11_out) == 0 && --time_out)
        {
            delay_us(1);
            low_bit++;
        }

        if (!time_out)
        {
            return -EAGAIN;
        }
        t2 = ktime_get_boot_ns() -t1;
        times[bit_index] = t2;
        databit0_count[bit_index] = low_bit;

        while (gpiod_get_value(dht11_out) == 1)
        {
            delay_us(1);
            hight_count+=3;
        }
        if (hight_count <= 40)
        {
            *byte = (*byte << 1) | 0;
            databit[bit_index] = 0;
        }
        else
        {
            *byte = *byte << 1 | 1;
            databit[bit_index] = 1;
        }
        databit1_count[bit_index] = hight_count;
        bit_index++;
    }
    return 1;
}

static void dth11_start(void)
{
    gpiod_direction_output(dht11_out, 1);
    mdelay(10);
    gpiod_set_value(dht11_out, 0);
    mdelay(20);
    gpiod_set_value(dht11_out, 1);
    udelay(30);
    gpiod_direction_input(dht11_out);
}
static int dth11_wait_ack(void)
{
    int time_out = 400;
    while (gpiod_get_value(dht11_out) == 1 && --time_out)
    {
        delay_us(1);
    }
    if (!time_out)
        return -EAGAIN;
    time_out = 400;
    while (gpiod_get_value(dht11_out) == 0 && --time_out)
    {
        delay_us(1);
    }
    if (!time_out)
        return -EAGAIN;
    time_out = 400;
    while (gpiod_get_value(dht11_out) == 1 && --time_out)
    {
        delay_us(1);
    }
    if (!time_out)
        return -EAGAIN;
    return 0;
}

/* 实现对应的open/read/write等函数，填入file_operations结构体                   */
static ssize_t dht11_drv_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{
    int i;
    int ret;
    unsigned long flags;
    local_irq_save(flags);
    bit_index = 0;
    dth11_start();

    ret = dth11_wait_ack();
    if (ret < 0)
    {
        local_irq_restore(flags);
        return ret;
    }
    for (i = 0; i < 5; i++)
    {
        ret = read_byte(&data[i]);
        if (ret < 0)
        {
            local_irq_restore(flags);
            return -EAGAIN;
        }
    }
    if (data[4] == data[0] + data[1] + data[2] + data[3])
    {
        copy_to_user(buf, data, 4);
        local_irq_restore(flags);
        for (i = 0; i < 40; i++)
        {
            printk("cout: %d, bit %d ,low : %d ,times : %d\n", databit1_count[i], databit[i], databit0_count[i],times[i]);
        }
        return 4;
    }
    else
    {
        local_irq_restore(flags);
        return -EAGAIN;
    }
}

static unsigned int dht11_drv_poll(struct file *fp, poll_table *wait)
{

    return 0;
}

static struct file_operations dht11_fops = {
    .owner = THIS_MODULE,
    .read = dht11_drv_read,
    .poll = dht11_drv_poll,
};

static int dht11_probe(struct platform_device *pdev)
{
    dht11_out = gpiod_get(&pdev->dev, "out", GPIOD_OUT_HIGH);
    if (IS_ERR(dht11_out))
    {
        printk("dht11_out error %s %d\n", __FUNCTION__, __LINE__);
        class_destroy(dht11_class);
        unregister_chrdev(major, "dht11");
        return PTR_ERR(dht11_out);
    }
    device_create(dht11_class, NULL, MKDEV(major, 0), NULL, "chen_dht11");

    return 0;
}

static int dht11_remove(struct platform_device *pdev)
{
    device_destroy(dht11_class, MKDEV(major, 0));
    gpiod_put(dht11_out);

    return 0;
}

static const struct of_device_id dht11_match[] = {
    {.compatible = "chen_dht11"},
    {},
};

static struct platform_driver dht11_drv = {
    .probe = dht11_probe,
    .remove = dht11_remove,
    .driver = {
        .name = "chen_dht11",
        .of_match_table = dht11_match,
    },
};
static int __init dht11_init(void)
{
    int err;

    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

    major = register_chrdev(0, "dht11", &dht11_fops);

    dht11_class = class_create(THIS_MODULE, "dht11_class");
    if (IS_ERR(dht11_class))
    {
        printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
        unregister_chrdev(major, "dht11");
        return PTR_ERR(dht11_class);
    }

    err = platform_driver_register(&dht11_drv);

    return err;
}

/* 3. 有入口函数就应该有出口函数：卸载驱动程序时，就会去调用这个出口函数
 *     卸载platform_driver
 */
static void __exit dht11_exit(void)
{
    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    platform_driver_unregister(&dht11_drv);
    class_destroy(dht11_class);
    unregister_chrdev(major, "dht11");
}

/* 7. 其他完善：提供设备信息，自动创建设备节点                                     */

module_init(dht11_init);
module_exit(dht11_exit);

MODULE_LICENSE("GPL");
